/*
 ------------------------------------------------------------------------------
 Copyright (C) 1996-2001 Id Software, Inc.

 This file is part of the Quake source code.

 The Quake source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quake source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quake source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// math.h - math library
//


#ifndef __MATH_MATH_H__
#define __MATH_MATH_H__


class	glqVec2;
class	glqVec3;
class	glqVec4;
class	glqVec5;
class	glqAngles;
class	glqQuat;
class	glqMat3;
class	glqMat4;
class	glqBounds;
class	glqSphere;
class	glqPlane;
class	glqColor;
class	glqRect;


#define M_E							2.71828182845904523536f
#define M_PI						3.14159265358979323846f
#define M_PI_TWO					6.28318530717958647692f
#define M_PI_HALF					1.57079632679489661923f
#define M_SQRT_TWO					1.41421356237309504880f
#define M_SQRT_THREE				1.73205080756887729352f
#define M_SQRT_1OVER2				0.70710678118654752440f
#define M_SQRT_1OVER3				0.57735026918962576450f
#define M_EPSILON					1.192092896e-07f
#define M_INFINITY					1e30f

#define CHARSIGNBITSET(c)			(((const uint)(c)) >> 7)
#define CHARSIGNBITNOTSET(c)		((~((const uint)(c))) >> 7)

#define SHORTSIGNBITSET(s)			(((const uint)(s)) >> 15)
#define SHORTSIGNBITNOTSET(s)		((~((const uint)(s))) >> 15)

#define INTSIGNBITSET(i)			(((const uint)(i)) >> 31)
#define INTSIGNBITNOTSET(i)			((~((const uint)(i))) >> 31)

#define FLOATSIGNBITSET(f)			((*(const uint *)&(f)) >> 31)
#define FLOATSIGNBITNOTSET(f)		((~(*(const uint *)&(f))) >> 31)

#define SEC2MS(t)					(glqMath::FloatToInt((t) * 1000.0f))
#define MS2SEC(t)					((t) * (1.0f / 1000.0f))

#define DEG2RAD(a)					((a) * M_PI / 180.0f)
#define RAD2DEG(a)					((a) * 180.0f / M_PI)

#define ANGLE2BYTE(a)				(glqMath::FloatToInt((a) * 256.0f / 360.0f) & 255)
#define BYTE2ANGLE(a)				((a) * (360.0f / 256.0f))

#define	ANGLE2SHORT(a)				(glqMath::FloatToInt((a) * 65536.0f / 360.0f) & 65535)
#define	SHORT2ANGLE(a)				((a) * (360.0f / 65536.0f))

#define UNITS2METERS(x)				((x) * 0.0254f)
#define METERS2UNITS(x)				((x) * (1.0f / 0.0254f))

template <class T> INLINE T			Min (T x, T y){ return (x < y) ? x : y; }
template <class T> INLINE T			Max (T x, T y){ return (x > y) ? x : y; }

template <class T> INLINE T			Min3 (T x, T y, T z){ return (x < y) ? ((x < z) ? x : z) : ((y < z) ? y : z); }
template <class T> INLINE T			Max3 (T x, T y, T z){ return (x > y) ? ((x > z) ? x : z) : ((y > z) ? y : z); }

template <class T> INLINE T			Square (T x){ return x * x; }
template <class T> INLINE T			Cube (T x){ return x * x * x; }

class glqMath {
public:
	static float		Sin (float x);
	static float		Cos (float x);
	static void			SinCos (float x, float &s, float &c);
	static float		Tan (float x);
	static float		ASin (float x);
	static float		ACos (float x);
	static float		ATan (float x);
	static float		ATan (float y, float x);
	static float		Log (float x);
	static float		Exp (float x);
	static float		Pow (float x, float y);
	static float		Sqrt (float x);
	static float		SqrtFast (float x);
	static float		RSqrt (float x);
	static float		Floor (float x);
	static float		Ceil (float x);
	static float		Round (float x);
	static float		Frac (float x);
	static float		FMod (float x, float y);
	static float		Sign (float x);
	static float		Lerp (float from, float to, float frac);
	static float		LerpFast (float from, float to, float frac);

	static int			Abs (int i);
	static float		FAbs (float f);

	static bool			IsPowerOfTwo (int i);
	static int			FloorPowerOfTwo (int i);
	static int			CeilPowerOfTwo (int i);

	static char			FloatToChar (float f);
	static byte			FloatToByte (float f);
	static short		FloatToShort (float f);
	static word			FloatToWord (float f);
	static int			FloatToInt (float f);

	static char			ClampChar (int value);
	static byte			ClampByte (int value);
	static short		ClampShort (int value);
	static word			ClampWord (int value);
	static int			ClampInt (int value, int min, int max);
	static float		ClampFloat (float value, float min, float max);

	static float		AngleNormalize360 (float angle);
	static float		AngleNormalize180 (float angle);
	static float		AngleDelta (float angle1, float angle2);

	static half			FloatToHalf (float f);
	static float		HalfToFloat (half h);

	static void			NormalToBytes (const glqVec3 &normal, byte bytes[2]);
	static void			BytesToNormal (const byte bytes[2], glqVec3 &normal);

	static void			ByteToDir (int b, glqVec3 &dir);
};

INLINE float glqMath::Sin (float x){

	return sinf(x);
}

INLINE float glqMath::Cos (float x){

	return cosf(x);
}

INLINE void glqMath::SinCos (float x, float &s, float &c){

#ifdef _MSC_VER

	__asm {
		fld			x
		mov			ecx, s
		mov			edx, c
		fsincos
		fstp		DWORD PTR [edx]
		fstp		DWORD PTR [ecx]
	}

#else

	s = sinf(x);
	c = cosf(x);

#endif
}

INLINE float glqMath::Tan (float x){

	return tanf(x);
}

INLINE float glqMath::ASin (float x){

	if (x <= -1.0f)
		return -M_PI_HALF;

	if (x >= 1.0f)
		return M_PI_HALF;

	return asinf(x);
}

INLINE float glqMath::ACos (float x){

	if (x <= -1.0f)
		return M_PI;

	if (x >= 1.0f)
		return 0.0f;

	return acosf(x);
}

INLINE float glqMath::ATan (float x){

	return atanf(x);
}

INLINE float glqMath::ATan (float y, float x){

	return atan2f(y, x);
}

INLINE float glqMath::Log (float x){

	return logf(x);
}

INLINE float glqMath::Exp (float x){

	return expf(x);
}

INLINE float glqMath::Pow (float x, float y){

	return powf(x, y);
}

INLINE float glqMath::Sqrt (float x){

	return sqrtf(x);
}

INLINE float glqMath::SqrtFast (float x){

#if defined SIMD_X86

	__m128	xmm;
	float	r;

	xmm = _mm_load_ss(&x);
	xmm = _mm_mul_ss(xmm, _mm_rsqrt_ss(xmm));
	_mm_store_ss(&r, xmm);

	return r;

#else

	float	y;
	float	r;
	int		i;

	y = x * 0.5f;
	i = *(int *)&x;
	i = 0x5F3759DF - (i >> 1);
	r = *(float *)&i;
	r = r * (1.5f - y * r * r);
	r = r * (1.5f - y * r * r);
	r = r * x;

	return r;

#endif
}

INLINE float glqMath::RSqrt (float x){

#if defined SIMD_X86

	__m128	xmm;
	float	r;

	xmm = _mm_load_ss(&x);
	xmm = _mm_rsqrt_ss(xmm);
	_mm_store_ss(&r, xmm);

	return r;

#else

	float	y;
	float	r;
	int		i;

	y = x * 0.5f;
	i = *(int *)&x;
	i = 0x5F3759DF - (i >> 1);
	r = *(float *)&i;
	r = r * (1.5f - y * r * r);
	r = r * (1.5f - y * r * r);

	return r;

#endif
}

INLINE float glqMath::Floor (float x){

	return floorf(x);
}

INLINE float glqMath::Ceil (float x){

	return ceilf(x);
}

INLINE float glqMath::Round (float x){

	return floorf(x + 0.5f);
}

INLINE float glqMath::Frac (float x){

	return x - floorf(x);
}

INLINE float glqMath::FMod (float x, float y){

	return fmodf(x, y);
}

INLINE float glqMath::Sign (float x){

	return (x >= 0.0f) ? 1.0f : -1.0f;
}

INLINE float glqMath::Lerp (float from, float to, float frac){

	if (frac <= 0.0f)
		return from;

	if (frac >= 1.0f)
		return to;

	return from + (to - from) * frac;
}

INLINE float glqMath::LerpFast (float from, float to, float frac){

	return from + (to - from) * frac;
}

INLINE int glqMath::Abs (int i){

	int		mask;

	mask = i >> 31;

	return (i ^ mask) - mask;
}

INLINE float glqMath::FAbs (float f){

	*(int *)&f &= 0x7FFFFFFF;

	return f;
}

INLINE bool glqMath::IsPowerOfTwo (int i){

	return (i > 0 && !(i & (i - 1)));
}

INLINE int glqMath::FloorPowerOfTwo (int i){

	i |= (i >> 1);
	i |= (i >> 2);
	i |= (i >> 4);
	i |= (i >> 8);
	i |= (i >> 16);

	i -= (i >> 1);

	return i + (i == 0);
}

INLINE int glqMath::CeilPowerOfTwo (int i){

	i--;
	i |= (i >> 1);
	i |= (i >> 2);
	i |= (i >> 4);
	i |= (i >> 8);
	i |= (i >> 16);
	i++;

	return i + (i == 0);
}

INLINE char glqMath::FloatToChar (float f){

#if defined SIMD_X86

	__m128	xmm;
	int		i;

	xmm = _mm_load_ss(&f);
	xmm = _mm_max_ss(xmm, _mm_set_ss(-128.0f));
	xmm = _mm_min_ss(xmm, _mm_set_ss(127.0f));
	i = _mm_cvtt_ss2si(xmm);

	return i;

#else

	int		i;

	i = (int)f;

	if (i < -128)
		return -128;

	if (i > 127)
		return 127;

	return i;

#endif
}

INLINE byte glqMath::FloatToByte (float f){

#if defined SIMD_X86

	__m128	xmm;
	int		i;

	xmm = _mm_load_ss(&f);
	xmm = _mm_max_ss(xmm, _mm_set_ss(0.0f));
	xmm = _mm_min_ss(xmm, _mm_set_ss(255.0f));
	i = _mm_cvtt_ss2si(xmm);

	return i;

#else

	int		i;

	i = (int)f;

	if (i < 0)
		return 0;

	if (i > 255)
		return 255;

	return i;

#endif
}

INLINE short glqMath::FloatToShort (float f){

#if defined SIMD_X86

	__m128	xmm;
	int		i;

	xmm = _mm_load_ss(&f);
	xmm = _mm_max_ss(xmm, _mm_set_ss(-32768.0f));
	xmm = _mm_min_ss(xmm, _mm_set_ss(32767.0f));
	i = _mm_cvtt_ss2si(xmm);

	return i;

#else

	int		i;

	i = (int)f;

	if (i < -32768)
		return -32768;

	if (i > 32767)
		return 32767;

	return i;

#endif
}

INLINE word glqMath::FloatToWord (float f){

#if defined SIMD_X86

	__m128	xmm;
	int		i;

	xmm = _mm_load_ss(&f);
	xmm = _mm_max_ss(xmm, _mm_set_ss(0.0f));
	xmm = _mm_min_ss(xmm, _mm_set_ss(65535.0f));
	i = _mm_cvtt_ss2si(xmm);

	return i;

#else

	int		i;

	i = (int)f;

	if (i < 0)
		return 0;

	if (i > 65535)
		return 65535;

	return i;

#endif
}

INLINE int glqMath::FloatToInt (float f){

#if defined SIMD_X86

	__m128	xmm;
	int		i;

	xmm = _mm_load_ss(&f);
	i = _mm_cvtt_ss2si(xmm);

	return i;

#else

	return (int)f;

#endif
}

INLINE char glqMath::ClampChar (int value){

	if (value < -128)
		return -128;

	if (value > 127)
		return 127;

	return value;
}

INLINE byte glqMath::ClampByte (int value){

	if (value < 0)
		return 0;

	if (value > 255)
		return 255;

	return value;
}

INLINE short glqMath::ClampShort (int value){

	if (value < -32768)
		return -32768;

	if (value > 32767)
		return 32767;

	return value;
}

INLINE word glqMath::ClampWord (int value){

	if (value < 0)
		return 0;

	if (value > 65535)
		return 65535;

	return value;
}

INLINE int glqMath::ClampInt (int value, int min, int max){

	if (value < min)
		return min;

	if (value > max)
		return max;

	return value;
}

INLINE float glqMath::ClampFloat (float value, float min, float max){

	if (value < min)
		return min;

	if (value > max)
		return max;

	return value;
}

INLINE float glqMath::AngleNormalize360 (float angle){

	if (angle < 0.0f || angle >= 360.0f)
		angle -= 360.0f * floorf(angle * (1.0f / 360.0f));

	return angle;
}

INLINE float glqMath::AngleNormalize180 (float angle){

	if (angle < 0.0f || angle >= 360.0f)
		angle -= 360.0f * floorf(angle * (1.0f / 360.0f));
	if (angle > 180.0f)
		angle -= 360.0f;

	return angle;
}

INLINE float glqMath::AngleDelta (float angle1, float angle2){

	float	angle;

	angle = angle1 - angle2;

	if (angle < 0.0f || angle >= 360.0f)
		angle -= 360.0f * floorf(angle * (1.0f / 360.0f));
	if (angle > 180.0f)
		angle -= 360.0f;

	return angle;
}


#endif	// __MATH_MATH_H__